package io.cici.cc.mybatis.lite.executor.statement;

import io.cici.cc.mybatis.lite.executor.Executor;
import io.cici.cc.mybatis.lite.executor.keygen.KeyGenerator;
import io.cici.cc.mybatis.lite.executor.parameter.ParameterHandler;
import io.cici.cc.mybatis.lite.executor.resultset.ResultContextHandler;
import io.cici.cc.mybatis.lite.mapping.MappedStatement;
import io.cici.cc.mybatis.lite.mapping.Sql;
import io.cici.cc.mybatis.lite.reflection.factory.ObjectFactory;
import io.cici.cc.mybatis.lite.session.Configuration;
import io.cici.cc.mybatis.lite.type.TypeHandlerRegistry;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

public abstract class BaseStatementHandler implements StatementHandler {

    protected final Configuration configuration;
    protected final ObjectFactory objectFactory;
    protected final TypeHandlerRegistry typeHandlerRegistry;
    protected final io.cici.cc.mybatis.lite.executor.resultset.ResultSetHandler resultSetHandler;
    protected final ParameterHandler parameterHandler;

    protected final Executor executor;
    protected final MappedStatement mappedStatement;

    protected Sql sql;

    protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject,
                                   ResultContextHandler resultContextHandler, Sql sql) {
        this.configuration = mappedStatement.getConfiguration();
        this.executor = executor;
        this.mappedStatement = mappedStatement;

        this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        this.objectFactory = Configuration.objectFactory;

        if (sql == null) { // issue #435, get the key before calculating the statement
            generateKeys(parameterObject);
            sql = mappedStatement.getSql(parameterObject);
        }

        this.sql = sql;

        this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, sql);
        this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, parameterHandler,
                resultContextHandler, sql);
    }

    @Override
    public Sql getBoundSql() {
        return sql;
    }

    @Override
    public ParameterHandler getParameterHandler() {
        return parameterHandler;
    }

    @Override
    public Statement prepare(Connection connection) throws SQLException {

        Statement statement = instantiateStatement(connection);
        return statement;
    }

    protected abstract Statement instantiateStatement(Connection connection) throws SQLException;

    protected void generateKeys(Object parameter) {
        KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();

        keyGenerator.processBefore(executor, mappedStatement, null, parameter);

    }

}
